package org.explugin.lang.parser.util;

import com.intellij.lang.PsiBuilder;
import org.explugin.ExBundle;
import org.explugin.lang.lexer.ExElementTypes;
import org.explugin.lang.lexer.ExTokenTypes;

public class FunctionParsing {

    protected static void parseFunction(PsiBuilder psiBuilder) {
        PsiBuilder.Marker functionDeclaration = psiBuilder.mark();
        psiBuilder.advanceLexer();

        // function name
        if (psiBuilder.getTokenType() == ExTokenTypes.IDENTIFIER) {
            PsiBuilder.Marker functionName = psiBuilder.mark();
            psiBuilder.advanceLexer();
            functionName.done(ExElementTypes.FUNCTION_NAME);
        }
        else {
            psiBuilder.error(ExBundle.message("explugin.error.expected.function.name"));
            functionDeclaration.drop();
            return;
        }

        // function parameters
        if (!parseFunctionParameters(psiBuilder)) {
            functionDeclaration.drop();
            return;
        }

        // function body
        if (!parseFunctionBody(psiBuilder)) {
            functionDeclaration.drop();
            return;
        }

        functionDeclaration.done(ExElementTypes.FUNCTION_DECLARATION);
    }

    private static boolean parseFunctionParameters(PsiBuilder psiBuilder) {

        if (psiBuilder.getTokenType() == ExTokenTypes.LPAREN_TOKEN) {
            PsiBuilder.Marker functionParameters = psiBuilder.mark();
            psiBuilder.advanceLexer();

            // function parameter
            boolean firstParameter = true;
            while (!psiBuilder.eof() && psiBuilder.getTokenType() != ExTokenTypes.RPAREN_TOKEN) {
                // require ',' between parameters
                if (!firstParameter) {
                    if (psiBuilder.getTokenType() == ExTokenTypes.COMMA_TOKEN) {
                        psiBuilder.advanceLexer();
                    }
                    else {
                        break;
                    }
                }
                if (!parseFunctionParameter(psiBuilder)) {
                    psiBuilder.advanceLexer();
                    break;
                }
                firstParameter = false;
            }

            if (ParsingUtil.checkBuilderEOF(psiBuilder, ExBundle.message("explugin.error.expected.rparen"), functionParameters)) {
                return false;
            }

            if (psiBuilder.getTokenType() == ExTokenTypes.RPAREN_TOKEN) {
                psiBuilder.advanceLexer();
                functionParameters.done(ExElementTypes.FUNCTION_PARAMETERS);
                return true;
            }

            psiBuilder.error(ExBundle.message("explugin.error.expected.rparen"));
            functionParameters.drop();
        }
        else {
            psiBuilder.error(ExBundle.message("explugin.error.expected.lparen"));
        }

        return false;
    }

    private static boolean parseFunctionParameter(PsiBuilder psiBuilder) {
        if (psiBuilder.getTokenType() == ExTokenTypes.IDENTIFIER) {
            PsiBuilder.Marker functionParameter = psiBuilder.mark();
            psiBuilder.advanceLexer();

            if (ParsingUtil.checkBuilderEOF(psiBuilder, ExBundle.message("explugin.error.expected.rparen"), functionParameter)) {
                return false;
            }

            if (psiBuilder.getTokenType() == ExTokenTypes.COLON_TOKEN) {
                psiBuilder.advanceLexer();

                if (ParsingUtil.checkBuilderEOF(psiBuilder, ExBundle.message("explugin.error.expected.function.parameter.type"), functionParameter)) {
                    return false;
                }

                if (psiBuilder.getTokenType() == ExTokenTypes.TYPE_TOKEN) {
                    psiBuilder.advanceLexer();
                }
                else {
                    psiBuilder.error(ExBundle.message("explugin.error.expected.function.parameter.type"));
                    functionParameter.drop();
                    return false;
                }
            }

            functionParameter.done(ExElementTypes.FUNCTION_PARAMETER);
            return true;
        }
        else {
            psiBuilder.error(ExBundle.message("explugin.error.expected.function.parameter"));
        }

        return false;
    }

    private static boolean parseFunctionBody(PsiBuilder psiBuilder) {

        if (psiBuilder.getTokenType() == ExTokenTypes.LCURLY_TOKEN) {
            PsiBuilder.Marker functionBody = psiBuilder.mark();
            psiBuilder.advanceLexer();

            while (!psiBuilder.eof() && psiBuilder.getTokenType() != ExTokenTypes.RCURLY_TOKEN) {
                StatementParsing.parseStatements(psiBuilder);
            }

            if (ParsingUtil.checkBuilderEOF(psiBuilder, ExBundle.message("explugin.error.expected.rcurly"), functionBody)) {
                return false;
            }

            if (psiBuilder.getTokenType() == ExTokenTypes.RCURLY_TOKEN) {
                psiBuilder.advanceLexer();
                functionBody.done(ExElementTypes.FUNCTION_BODY);
                return true;
            }
        }
        else {
            psiBuilder.error(ExBundle.message("explugin.error.expected.lcurly"));
        }

        return false;
    }
}
